package com.lagou.rpc.provider.handler;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.provider.annotation.RpcService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 服务端业务处理类
 * 1、将标有@RpcService的注解的bean缓存
 * 2、接收客户端请求
 * 3、根据传递过来的beanName从缓存中查找对应的bean
 * 4、解析请求中的方法名称，参数类型 参数信息
 * 5、反射调用bean的方法
 * 6、给客户的进行响应
 *
 * Created by wangq on 2021/8/29.
 */
@Component
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {
    private static final Map SERVICE_INSTANCE_MAP = new HashMap();

    /**
     * 通道读取就绪事件
     *
     * @param channelHandlerContext
     * @param msg                   读取的消息
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        // 2、接收客户端请求 将msg转换为RpcRequest
        RpcRequest rpcRequest = JSON.parseObject(msg, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());

        // 业务处理
        try {
            Object handler = handler(rpcRequest);
            rpcResponse.setResult(handler);
        } catch (Exception exception) {
            exception.printStackTrace();
            rpcResponse.setError(exception.getMessage());
        }

        // 6、给客户的进行响应
        channelHandlerContext.writeAndFlush(JSON.toJSONString(rpcResponse));
    }

    /**
     * 业务处理逻辑
     *
     * @param rpcRequest
     * @return
     */
    public Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        //3、根据传递过来的beanName从缓存中查找对应的bean
        Object serviceBean = SERVICE_INSTANCE_MAP.get(rpcRequest.getClassName());
        if (serviceBean == null) {
            throw new RuntimeException("根据beanName找不到服务，beanName:" + rpcRequest.getClassName());
        }
        //4、解析请求中的方法名称，参数类型 参数信息
        Class<?> serviceBeanClass = serviceBean.getClass();
        String methodName = rpcRequest.getMethodName();
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        Object[] parameters = rpcRequest.getParameters();

        // 5、反射调用bean的方法 使用cglib反射调用
        FastClass fastClass = FastClass.create(serviceBeanClass);
        FastMethod fastMethod = fastClass.getMethod(methodName, parameterTypes);

        return fastMethod.invoke(serviceBean, parameters);
    }

    /**
     * 1、将标有@RpcService的注解的bean缓存
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if (serviceMap != null && serviceMap.size() > 0) {
            for (Map.Entry<String, Object> entry : serviceMap.entrySet()) {
                Object serviceBean = entry.getValue();
                Class<?>[] serviceInterfaces = serviceBean.getClass().getInterfaces();
                if (serviceInterfaces.length == 0) {
                    throw new RuntimeException("服务必须实现接口");
                }
                // 默认去第一个接口作为缓存的名称
                String beanName = serviceInterfaces[0].getName();
                SERVICE_INSTANCE_MAP.put(beanName, serviceBean);
            }
        }

    }
}
